﻿/**
 单元检查人签名：cxs
 单元检查日期：2014-7-25
 
 */
using UnityEngine;
using System.Collections;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System;



/*
    类描述：游戏数据读写器。本函数读取excel的xls文件中的sheet导出的数据  
 
 
    调用示例:
        using (GameDataTableParser gameDataTable = new GameDataTableParser()) //注意：一定要using()，这样才会自动调用这个类的Dispose()方法！！！！！！！
        {
                gameDataTable.ReadTableFromTextAsset(tx, true);

                gameDataTable.first();
                while (!gameDataTable.eof())
                {
                    Debug.Log("==新记录==============");
                    Debug.Log( gameDataTable.GetFieldByName("buc"));
                    gameDataTable.next();
                }

                if (gameDataTable.locateTo("buc", "不是"))
                {
                    Debug.Log("定位到记录后="+gameDataTable.GetFieldByName("zheg"));
                }
         }
*/
/// <summary>
/// 数据表解析器
/// </summary>
public class GameDataTableParser : IDisposable
{
    //-----------------------------------
    //字段信息
    public string[] FieldNameList;//字段名列表，注意：全部是小写
    public string[] FieldDescriptionList;//字段名注释信息列表
    public bool[] FieldPkInfoList;//字段的主键信息
    public XlsFieldTypeID[] XlsFieldTypeIDList;//字段的数据类型列表

    //记录信息
    public string[,] FieldValue;//数据记录二维数组，第一维是行号，第二维是列号
    public int CurRowNo = 0;//当前行号，从0开始计数
    //-----------------------------------

    private const int FileFlagConst = 0x21007743;//文件标识


    #region 按行操作
    /// <summary>
    /// 当前记录号定位到开头
    /// </summary>
    public void first()
    {
        CurRowNo = 0;//当前行号设置到第一行记录
    }

    /// <summary>
    /// 定位到最后一条记录
    /// </summary>
    public void last()
    {
        CurRowNo = getRecordCount() - 1;
    }

    /// <summary>
    /// 判断是否超过了开头
    /// </summary>
    /// <returns></returns>
    public bool bof()
    {
        return (CurRowNo < 0);
    }

    /// <summary>
    /// 判断是否超过了结尾
    /// </summary>
    /// <returns></returns>
    public bool eof()
    {
        return (CurRowNo >= getRecordCount());
    }

    /// <summary>
    /// 转到下一条记录
    /// </summary>
    public void next()
    {
        if (this.eof()) return; //如果超过了结尾直接返回
        CurRowNo++;
    }

    /// <summary>
    /// 转到上一条记录
    /// </summary>
    public void prior()
    {
        if (this.bof()) return;
        CurRowNo--;
    }

    /// <summary>
    /// 定位到指定记录（备注：本函数适合只有一个主键字段的情况！）
    /// </summary>
    /// <param name="KeyFieldName">主键字段的字段名</param>
    /// <param name="KeyFieldValue">主键字段的字段值</param>
    /// <returns>是否定位成功</returns>
    public bool locateTo(string KeyFieldName, string KeyFieldValue)
    {
        int FieldNo = getFieldIdx(KeyFieldName);//取出字段序号
        if (FieldNo == -1) return false;//如果字段不存在则返回""

        string ss_KeyFieldValue = KeyFieldValue.ToLower();
        for (int row = 0; row < this.getRecordCount(); row++)
        {
            if (this.FieldValue[row, FieldNo].ToLower() == ss_KeyFieldValue)
            {
                this.CurRowNo = row;//把row行设置为当前行号
                return true;
            }
        }

        return false;
    }


    /// <summary>
    /// 定位到指定记录（备注：本函数适合两个或两个以上主键字段的情况！）
    /// </summary>
    /// <param name="KeyFieldName">主键字段的字段名列表</param>
    /// <param name="KeyFieldValue">主键字段的字段值列表</param>
    /// <returns>是否定位成功</returns>
    public bool locateTo(string[] KeyFieldName, string[] KeyFieldValue)
    {
        //获取字段名所属列序号列表
        int[] FieldNo = new int[KeyFieldName.Length];
        for (int i = 0; i < KeyFieldName.Length; i++)
        {
            FieldNo[i] = getFieldIdx(KeyFieldName[i]);//取出字段序号
            if (FieldNo[i] == -1) return false;//如果字段不存在则返回""    
        }


        for (int row = 0; row < this.getRecordCount(); row++)
        {
            bool b = true;
            for (int i = 0; i < KeyFieldName.Length; i++)
            {
                if (this.FieldValue[row, FieldNo[i]].ToLower() != KeyFieldValue[i].ToLower())
                {
                    b = false;
                    break;
                }
            }


            if (b)
            {
                this.CurRowNo = row;//把row行设置为当前行号
                return true;
            }
        }

        return false;
    }


    /// <summary>
    /// 获取当前行的指定字段的值-----按字符串
    /// </summary>
    /// <param name="FieldName">字段名</param>
    /// <returns>当前行指定字段的值，如果获取失败，则返回""</returns>
    public string GetFieldByNameAsString(string FieldName)
    {
        int FieldNo = getFieldIdx(FieldName);//取出字段序号
        if (FieldNo == -1) return "";//如果字段不存在则返回""

        if (this.bof() || this.eof()) return "";//如果是bof或eof，则直接返回""

        return this.FieldValue[this.CurRowNo, FieldNo];//获取当前行指定字段的值        
    }

    /// <summary>
    /// 获取当前行的指定字段的值---按int类型
    /// </summary>
    /// <param name="FieldName">字段名</param>
    /// <returns>当前行指定字段的值，如果获取失败，则返回""</returns>
    public int GetFieldByNameAsInt(string FieldName)
    {
        int temp = 0;
        Int32.TryParse(this.GetFieldByNameAsString(FieldName).Trim(), out temp);
        return temp;
    }

    /// <summary>
    /// 获取当前行的指定字段的值---按float类型
    /// </summary>
    /// <param name="FieldName">字段名</param>
    /// <returns>当前行指定字段的值，如果获取失败，则返回""</returns>
    public float GetFieldByNameAsFloat(string FieldName)
    {
        float temp = 0;
        float.TryParse(this.GetFieldByNameAsString(FieldName).Trim(), out temp);
        return temp;
    }

    public string GetFieldDescriptionByName(string fieldName)
    {
        int FieldNo = getFieldIdx(fieldName);//取出字段序号
        if (FieldNo == -1) return "";//如果字段不存在则返回""
        return this.FieldDescriptionList[FieldNo];//获取当前行指定字段的值
    }
    /// <summary>
    /// 获取指定字段的数据类型
    /// </summary>
    /// <param name="FieldName">字段名</param>
    /// <returns>字段的数据类型</returns>
    public XlsFieldTypeID getFieldTypeByName(string FieldName)
    {
        int FieldNo = getFieldIdx(FieldName);//取出字段序号
        if (FieldNo == -1) return XlsFieldTypeID.XlsFieldTypeID_NULL;//如果字段不存在则返回""

        return this.XlsFieldTypeIDList[FieldNo];//获取当前行指定字段的值        
    }

    /// <summary>
    /// 判断指定字段的是否是主键
    /// </summary>
    /// <param name="FieldName">字段名</param>
    /// <returns>是否是主键字段</returns>
    public bool IsPKField(string FieldName)
    {
        int FieldNo = getFieldIdx(FieldName);//取出字段序号
        if (FieldNo == -1) return false;

        return this.FieldPkInfoList[FieldNo];//判断字段是否是主键字段
    }
    /// <summary>
    /// 获取主键名
    /// </summary>
    /// <returns></returns>
    public string GetPrimaryKeyName()
    {
        string pk = "";
        for (int c = 0; c < FieldNameList.Length; c++)
        {
            if (FieldPkInfoList[c])
                pk = FieldNameList[c];
        }
        return pk;
    }

    #endregion






    #region 一般操作
    /// <summary>
    /// 获取字段总数
    /// </summary>
    /// <returns></returns>
    public int getFieldCount()
    {
        if (this.FieldValue != null)
            return this.FieldValue.GetLength(1);
        else
            return 0;
    }

    /// <summary>
    /// 获取记录个数
    /// </summary>
    /// <returns></returns>
    public int getRecordCount()
    {
        if (this.FieldValue != null)
            return this.FieldValue.GetLength(0);
        else
            return 0;
    }

    /// <summary>
    /// 获取指定名称的字段的序号。如果没有找到则返回-1
    /// </summary>
    /// <param name="FieldName"></param>
    /// <returns></returns>
    private int getFieldIdx(string FieldName)
    {
        FieldName = FieldName.ToLower();//转换为小写，重要！因为FieldNameList中的全部是小写，这样我们就可以实现“忽略大小写”了

        for (int i = 0; i < FieldNameList.Length; i++)
        {
            if (FieldNameList[i] == FieldName)
            {
                return i;
            }
        }

        return -1;
    }

    /// <summary>
    /// 获取指定行号列号的记录
    /// </summary>
    /// <param name="FieldName">字段名称，备注：比较的时候，我们忽略了大小写了</param>
    /// <param name="RowNo">行号，从0开始计数</param>
    /// <returns>指定单元格的值，如果没有找到则返回空字符串</returns>
    public string getItem(string FieldName, int RowNo)
    {
        int FieldNo = getFieldIdx(FieldName);//取出字段序号

        if (FieldNo == -1) return "";//如果字段不存在则返回""
        if (RowNo < 0 || RowNo >= this.getRecordCount()) return "";//如果行号不合法则返回""

        return this.FieldValue[RowNo, FieldNo];
    }
    #endregion


    #region  从文件读取
    /// <summary>
    /// 从TextAsset中读取表格信息
    /// </summary>
    /// <param name="dataFile">TextAsset文件，这个文件的扩展名应该是.bytes</param>
    /// <param name="Debuglog">是否输出调试信息</param>
    /// <returns></returns>
    public bool ReadTableFromTextAsset(TextAsset dataFile, bool Debuglog = false)
    {
        if (dataFile == null) return false;

        return ReadTableFromBytes(dataFile.bytes, Debuglog);
    }

    private static byte[] xorScale = new byte[] { 0x11, 0x9A, 0x1c, 0x22, 0x08, 0xE0, 0x21, 0x19 };//.bytes文件的xor加解密因子

    /// <summary>
    /// 从字节数数组，读取表格
    /// </summary>
    /// <param name="fileBytes">带解析的byte数组</param>
    /// <param name="Debuglog">是否输出调试信息</param>
    /// <returns></returns>
    public bool ReadTableFromBytes(byte[] fileBytes, bool Debuglog = false)
    {
        if (fileBytes == null) return false;


        //------------------
        //第1步：xor解密
        //------------------
        int iScaleLen = xorScale.Length;
        for (int i = 0; i < fileBytes.Length; i++)
        {
            fileBytes[i] = (byte)(fileBytes[i] ^ xorScale[i % iScaleLen]);
        }

        //------------------
        //第2部：解压缩
        //------------------
        byte[] orgBytes = ZlibHelper.DeCompressBytesToBytes(fileBytes);//解压缩zlib字节数组
        if (orgBytes == null) return false;//如果解压zlib字节数组失败，则直接返回







        //------------------
        //读取内容
        //------------------
        CurRowNo = 0;//当前行号
        using (MemoryStream_LittleEndian memoryStream = new MemoryStream_LittleEndian(orgBytes))
        {
            memoryStream.Position = 0;

            //--------------------------
            //读取文件标志，判断是否合法
            //--------------------------
            int fileFlag = memoryStream.ReadInt();
            if (fileFlag != FileFlagConst) return false;

            //--------------------------
            //读取出行数（记录数）、列数（字段数）
            //--------------------------
            int iRowCount = memoryStream.ReadInt();//行数
            int iColCount = memoryStream.ReadInt();//列数

            //Debug.Log("iRowCount=" + iRowCount);
            //Debug.Log("iColCount=" + iColCount);


            //字段信息
            this.FieldNameList = new string[iColCount];//字段名列表
            this.FieldPkInfoList = new bool[iColCount];//字段的主键信息
            this.XlsFieldTypeIDList = new XlsFieldTypeID[iColCount];//字段的数据类型列表
            this.FieldDescriptionList = new string[iColCount];//字段描述列表
            //记录信息
            this.FieldValue = new string[iRowCount, iColCount];//数据记录二维数组，第一维是行号，第二维是列号

            //--------------------------
            //读取字段名列表
            //--------------------------
            for (int c = 0; c < iColCount; c++)
            {
                //转换为字符串
                FieldNameList[c] = memoryStream.ReadUTF8String().ToLower();//转换为小写，重要！以后我们比较的时候，被比较的字符串也转换为小写了，这样我们就可以实现“忽略大小写”了
                FieldPkInfoList[c] = (memoryStream.ReadByte() == 1);//是否是主键
                XlsFieldTypeIDList[c] = (XlsFieldTypeID)memoryStream.ReadByte();//字段的数据类型列表
      //          FieldDescriptionList[c] = memoryStream.ReadUTF8String();
            }

            //--------------------------
            //读取各个记录
            //--------------------------
            for (int r = 0; r < iRowCount; r++)
            {
                for (int c = 0; c < iColCount; c++)
                {
                    //转换为字符串
                    FieldValue[r, c] = memoryStream.ReadUTF8String();
                }
            }


            memoryStream.Close();
        }
        orgBytes = null;


#if UNITY_EDITOR
        //--------
        //输出调试信息
        //--------
        if (Debuglog)
        {
            showContent();
        }
#endif

        return true;
    }

#if UNITY_EDITOR
    /// <summary>
    /// 输出调试信息
    /// </summary>
    void showContent()
    {
        //Debug.Log("行数=" + this.getRecordCount());
        //Debug.Log("列数=" + this.getFieldCount());

        //----------
        //字段名
        //----------
        string FieldNameListStr = "";
        for (int c = 0; c < FieldNameList.Length; c++)
        {
            //字段名称
            FieldNameListStr += FieldNameList[c];

            //字段的主键信息
            if (FieldPkInfoList[c]) FieldNameListStr += "[主键]";

            //字段类型
            if (XlsFieldTypeIDList[c] == XlsFieldTypeID.XlsFieldTypeID_int) FieldNameListStr += "(int)";
            if (XlsFieldTypeIDList[c] == XlsFieldTypeID.XlsFieldTypeID_float) FieldNameListStr += "(float)";
            if (XlsFieldTypeIDList[c] == XlsFieldTypeID.XlsFieldTypeID_string) FieldNameListStr += "(string)";

            if (c < FieldNameList.Length - 1) FieldNameListStr += ",";

        }
        //Debug.Log("字段名列表：" + FieldNameListStr);

        //----------
        //所有数据
        //----------
        //Debug.Log("数值begin-------------");
        for (int r = 0; r < this.getRecordCount(); r++)
        {
            string s = "";
            for (int c = 0; c < this.getFieldCount(); c++)
            {
                s += FieldValue[r, c];
                if (c < this.getFieldCount() - 1)
                {
                    s += "，";
                }
            }
            //Debug.Log(s);
        }
        //Debug.Log("数值end-------------");
    }
#endif

    /// <summary>
    /// 释放数据
    /// </summary>
    public void Dispose()
    {
        this.FieldNameList = null;
        this.FieldValue = null;

        FieldPkInfoList = null;
        XlsFieldTypeIDList = null;
    }


    #endregion
}

/// <summary>
/// 字段的数据类型
/// </summary>
public enum XlsFieldTypeID
{
    XlsFieldTypeID_NULL = -1,//未知

    XlsFieldTypeID_int = 0,//字段的数据类型是整数
    XlsFieldTypeID_float = 1,//字段的数据类型是浮点数
    XlsFieldTypeID_string = 2//字段的数据类型是字符串
}
